#define LOG_TAG "CONNECTIVITY_TEST_SERVICE"
#include <stdio.h>
#include <stdlib.h>
#include <log/log.h>
#include <pthread.h>
#include <ipcp/ipcp.h>
#include <chrono>
#include <thread>
#include <unistd.h>

#include "OpESIMConnectionStatus.h"
#include "OpSyncTCAMinformation.h"
#include "OpWLANAPConnDevList.h"
#include "OpWLANAPSETTING.h"
#include "OpWLANLOGSETTING.h"
#include "OpWLANLOGSETTINGReq.h"
#include "OpWLANMode.h"
#include "OpWLANModeReq.h"
#include "OpWLANSTAConnect.h"
#include "OpWLANSTADisCon.h"
#include "OpWLANSTAForgetRememberedNetwk.h"
#include "OpWLANSTARememberedNetwks.h"
#include "OpWLANSTAScan.h"
#include "OpWLANSTAStatus.h"
#include "CommonElements.h"

#define LOCAL_IP "127.0.0.1"
#define LOCAL_PORT 5555

#define PEER_IP "127.0.0.1"
#define PEER_PORT 7777

#define ASN_HEADER_LENGTH_OFFSET 8

// Define Service ID
#define SERVICE_ID_CONNECTIVITY       0xB1

#define SERVICE_VLAN_ID               (2)
#define SERVICE_VLAN_PCP              (0)

// Define Operation Type
#define OP_TYPE_REQUEST               0x00
#define OP_TYPE_SET_REQUEST_NoReturn  0x01
#define OP_TYPE_SET_REQUEST           0x02
#define OP_TYPE_RESPONSE              0x04
#define OP_TYPE_NOTIFICATION          0x05
#define OP_TYPE_NOTIFICATION_CYCLIC   0x06
#define OP_TYPE_ERROR                 0xe0

enum {
    OP_ID_OpESIMConnectionStatus = 0x0001,
    OP_ID_OpWLANMode = 0x0011,
    OP_ID_OpWLANAPSETTING = 0x0012,
    OP_ID_OpWLANAPConnDev = 0x0013,
    OP_ID_OpWLANSTAConnect = 0x0014,
    OP_ID_OpWLANSTAStatus = 0x0015,
    OP_ID_OpWLANSTARememberedNetwks = 0x0016,
    OP_ID_OpWLANSTAForgetRememberedNetwk = 0x0017,
    OP_ID_OpWLANSTAScan = 0x0019,
    OP_ID_OpWLANLOGSETTING = 0x001A,
    OP_ID_SyncTCAMinformation = 0x001B,
    OP_ID_OpWLANModeReq = 0x001C,
    OP_ID_OpWLANLogSettingReq = 0x001D,
    OP_ID_OpWLANSTADisCon = 0x0020
};



static int signal_strength_count = 20;
static ipcp_peer_t peer_server = {
    .sk_type = IPCP_TRANSPORT_UDP,
    .peer_ip = PEER_IP,
    .peer_port = PEER_PORT,
};

void received_cb(ipcp_packet_t *packet, ipcp_peer_t peer);
void error_cb(ipcp_packet_t *ipcp_packet, ipcp_stack_err_t error);

ipcp_callbacks_t callbacks = {
    .ipcp_received_cb = received_cb,
    .error_cb = error_cb,
};

char *osi_strdup(const char *str) {
    size_t size = strlen(str) + 1;  // + 1 for the null terminator
    //size_t real_size = allocation_tracker_resize_for_canary(size);
    char *new_string = (char *)malloc(size);

    memcpy(new_string, str, size);
    return new_string;
}

void received_cb(ipcp_packet_t *packet, ipcp_peer_t peer) {
    ALOGI("received_cb <<<<<<<<<<<<< ");
    ipcp_header_t *header = &packet->header;
    uint16_t len = header->length - 8;
    int stat;
    ALOGI("%s, received packet len %u \n", __func__, header->length);
    ALOGI("%s, received payload len %hu \n", __func__, len);
    ALOGI("%s, received ipcp header : sid: %#x, sender_handler_id : %#x, sendor op_id : %#x \n",
          __func__, packet->header.service_id, packet->header.senderHandleId,
          packet->header.operation_id);

    switch (header->operation_id)
    {
        case OP_ID_OpWLANMode:
        {
            ALOGI("RCV <<<< OpWLANMode");
            if (header->op_type == OP_TYPE_SET_REQUEST_NoReturn) {

                ASN1PERDecodeBuffer decodeBuffer((OSOCTET *)packet->payload, len, false);
                ASN1T_OpWLANMode_SetRequestNoReturn data;
                ASN1C_OpWLANMode_SetRequestNoReturn OpWLANMode_SetRequestNoReturn(decodeBuffer, data);
                stat = OpWLANMode_SetRequestNoReturn.Decode();
                if (stat != 0)
                {
                    ALOGD("setBand decode error %d",stat);
                    decodeBuffer.PrintErrorInfo ();
                    break;
                } else
                {
                    ALOGD("%s WLAN MODE SetRequest decode success, Mode: %d,\n", __FUNCTION__, data.functionalControl);
                }

                // Response to client.
                int stat, len;
                ipcp_stack_err_t err;
                const char *content = NULL;

                ASN1PEREncodeBuffer encodeBuffer(false);
                ASN1T_OpWLANMode_Notification dataResponse;

                dataResponse.functionalControl = 0x03;
                ASN1C_OpWLANMode_Notification OpWLANMode_Notification(encodeBuffer, dataResponse);

                stat = OpWLANMode_Notification.Encode();
                if (stat == 0)
                {
                    ALOGI("setRequest WLAN Mode response encode success");

                    len = encodeBuffer.getMsgLen();
                    content = (char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t *result_packet = build_ipcp_packet(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANMode,
                                                                       OP_TYPE_NOTIFICATION, IPCP_DATA_ENCODED,
                                                                       (void *)content, len);
                    if ((err = ipcp_send(peer_server, result_packet)) != STACK_SEND_MSG_SUCCESS)
                    {
                        ALOGE("%s: ipcp_send err : %d", __FUNCTION__, err);
                        break;
                    }
                    else
                    {
                        ALOGI("ipcp send response success");
                    }
                }
                else
                {
                    ALOGI("setRequest WLAN Mode response encode failed %d", stat);
                    encodeBuffer.printErrorInfo();
                    break;
                }
            }
            break;
        }
        case OP_ID_OpWLANAPSETTING:
        {
            if (header->op_type == OP_TYPE_REQUEST) {

                int stat, len;
                const char *content = NULL;

                ASN1PEREncodeBuffer encodeBuffer(false);
                ASN1T_OpWLANAPSETTING_Response response;

                response.wl_AP_SSID = reinterpret_cast<const OSUTF8CHAR *>("WTF-WTF");
                response.wl_AP_PASS = reinterpret_cast<const OSUTF8CHAR *>("12345678");
                response.frequencyBand = 3;
                response.frequencyChannel = 6;
                response.ap_ipadr = ASN1T_OpWLANAPSETTING_Response_ap_ipadr("1111");
                response.ap_Macadr = ASN1T_OpWLANAPSETTING_Response_ap_Macadr("ABCD");

                ASN1C_OpWLANAPSETTING_Response OpWLANAPSETTING_Response(encodeBuffer, response);

                if (!OpWLANAPSETTING_Response.Encode()) {
                    len = encodeBuffer.getMsgLen();
                    content = (const char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t * packet = build_ipcp_response(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANAPSETTING,
                                                            header->senderHandleId, IPCP_DATA_ENCODED,
                                                            (void *)content, len);
                    ipcp_send(peer_server, packet);
                }
            }

            break;
        }
        case OP_ID_OpWLANAPConnDev:
        {
            break;
        }
        case OP_ID_OpWLANSTAConnect:
        {
            if (header->op_type == OP_TYPE_SET_REQUEST_NoReturn) {
                ASN1PERDecodeBuffer decodeBuffer((OSOCTET *)packet->payload, len, false);
                ASN1T_OpWLANSTAConnect_SetRequestNoReturn setRequest_noReturn;
                ASN1C_OpWLANSTAConnect_SetRequestNoReturn OpWLANSTAConnect_SetRequestNoReturn(decodeBuffer, setRequest_noReturn);

                if (!OpWLANSTAConnect_SetRequestNoReturn.Decode()) {

                    ALOGI("recevied STA Connect SSID is %s\n", reinterpret_cast<const char*>(setRequest_noReturn.wl_AP_SSID));

                    int stat, len;
                    const char *content = nullptr;

                    ASN1PEREncodeBuffer encodeBuffer(false);
                    ASN1T_OpWLANSTAConnect_Notification notification;

                    notification.connectedResult = true;
                    notification.failure_ReasonCode = 0;
                    notification.wl_AP_SSID = (setRequest_noReturn.wl_AP_SSID);

                    ASN1C_OpWLANSTAConnect_Notification OpWLANSTAConnect_Notification(encodeBuffer, notification);
                    if (!OpWLANSTAConnect_Notification.Encode()) {
                        len = encodeBuffer.getMsgLen();
                        content = (const char *) (encodeBuffer.getMsgPtr());

                        ipcp_packet_t * packet = build_ipcp_packet(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANSTAConnect,
                                                                IPCP_OPERATION_NOTIFICATION, IPCP_DATA_ENCODED,
                                                                (void *)content, len);

                        ipcp_send(peer_server, packet);
                    }

                }
            }
            break;
        }
        case OP_ID_OpWLANSTAStatus:
        {
            if (header->op_type == OP_TYPE_REQUEST) {

                int stat, len;
                const char *content = nullptr;

                ASN1PEREncodeBuffer encodeBuffer(false);
                ASN1T_OpWLANSTAStatus_Response response;

                ASN1T_Available_AP_INFO ap_info;
                ap_info.available_AP_SSID = reinterpret_cast<const OSUTF8CHAR *>("WTK-123");
                ap_info.ap_Macadr = ASN1T_Available_AP_INFO_ap_Macadr("ABCD");
                ap_info.conn_Status = true;
                ap_info.frequencyBand = 1;
                ap_info.rssi = 50;
                ap_info.sec_Type = 2;

//                ASN1C_OpWLANSTAStatus_Response_avaliableNetwork Response_avaliableNetwork(response.avaliableNetwork);
//                Response_avaliableNetwork.Append(&ap_info);

                response.avaliableNetwork = ap_info;

                ASN1C_OpWLANSTAStatus_Response OpWLANSTAStatus_Response(encodeBuffer, response);
                if (!OpWLANSTAStatus_Response.Encode()) {

                    len = encodeBuffer.getMsgLen();
                    content = (const char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t * packet = build_ipcp_response(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANSTAStatus,
                                                                header->senderHandleId, IPCP_DATA_ENCODED,
                                                                (void *)content, len);

                    ipcp_send(peer_server, packet);
                }
            }
            break;
        }
        case OP_ID_OpWLANSTARememberedNetwks:
        {
            if ((header->op_type == OP_TYPE_REQUEST) && (header->length == 0x08)) {
                ALOGI("It's a Generic Request");
            }

            // Response to client.
            int stat, len;
            ipcp_stack_err_t err;
            const char *content = NULL;

            ASN1PEREncodeBuffer encodeBuffer(false);
            ASN1T_OpWLANSTARememberedNetwks_Response data;
            ASN1T_OpWLANSTARememberedNetwks_Response_rememberedNetwork networks;

            ASN1T_Remembered_AP_INFO remebered_ap_info;
            remebered_ap_info.available_AP_SSID = reinterpret_cast<const OSUTF8CHAR *> ("abc");
            remebered_ap_info.frequencyBand = 1;
            remebered_ap_info.sec_Type = 2;
            
            ASN1C_OpWLANSTARememberedNetwks_Response_rememberedNetwork Response_rememberedNetwork(networks);
            Response_rememberedNetwork.Append(&remebered_ap_info);
            
            data.rememberedNetwork = networks;

            ASN1C_OpWLANSTARememberedNetwks_Response OpWLANSTARememberedNetwks_Response(encodeBuffer, data);

            stat = OpWLANSTARememberedNetwks_Response.Encode();
            if (stat == 0) {
                    len = encodeBuffer.getMsgLen();
                    content = (char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t *result_packet = build_ipcp_response(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANSTARememberedNetwks,
                                                                       header->senderHandleId, IPCP_DATA_ENCODED,
                                                                       (void *)content, len);
                    if ((err = ipcp_send(peer_server, result_packet)) != STACK_SEND_MSG_SUCCESS)
                    {
                        ALOGE("%s: ipcp_send err : %d", __FUNCTION__, err);
                        break;
                    }
            }

            break;
        }
        case OP_ID_OpWLANSTAForgetRememberedNetwk:
        {
            break;
        }
        case OP_ID_OpWLANSTAScan:
        {
            if (header->op_type == OP_TYPE_REQUEST) {

                int stat, len;
                const char *content = nullptr;

                ASN1PEREncodeBuffer encodeBuffer(false);
                ASN1T_OpWLANSTAScan_Response response;

                ASN1T_Current_AP_INFO ap_info;
                ap_info.available_AP_SSID = reinterpret_cast<const OSUTF8CHAR *>("Hello Alvin");
                ap_info.m.ap_MacadrPresent = 1;
                ap_info.ap_Macadr.numocts = 6;

                for (int i = 0; i < (int)(ap_info.ap_Macadr.numocts); i++) {
                    ap_info.ap_Macadr.data[i] = 0x01;
                    ALOGE("%s, bssid[%#x] : %#x", __func__, i, ap_info.ap_Macadr.data[i]);
                }

                ap_info.frequencyBand = 1;
                ap_info.rssi = 30;
                ap_info.sec_Type = 2;

                ASN1T_Current_AP_INFO ap_info2;
                ap_info2.available_AP_SSID = reinterpret_cast<const OSUTF8CHAR *>("WTF --- ***8888");
                ap_info2.m.ap_MacadrPresent = 1;
                ap_info2.ap_Macadr.numocts = 6;

                for (int i = 0; i < (int)(ap_info2.ap_Macadr.numocts); i++) {
                    ap_info2.ap_Macadr.data[i] = 0xBB;
                }

                ap_info2.frequencyBand = 3;
                ap_info2.rssi = 90;
                ap_info2.sec_Type = 2;

                ASN1C_OpWLANSTAScan_Response_scanResults scanResults(response.scanResults);
                scanResults.Append(&ap_info);
                scanResults.Append(&ap_info2);

                ASN1C_OpWLANSTAScan_Response OpWLANSTAScan_Response(encodeBuffer, response);
                if (!OpWLANSTAScan_Response.Encode()) {
                    len = encodeBuffer.getMsgLen();
                    content = (const char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t * packet = build_ipcp_response(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANSTAScan,
                                                                header->senderHandleId, IPCP_DATA_ENCODED,
                                                                (void *)content, len);

                    ipcp_send(peer_server, packet);
                }
            }
            break;
        }
        case OP_ID_OpWLANLOGSETTING:
        {
            break;
        }
        case OP_ID_SyncTCAMinformation:
        {
            break;
        }
        case OP_ID_OpWLANModeReq:
        {

            if (header->op_type == OP_TYPE_REQUEST) {
                if (header->length == 0x08) {
                    ALOGI("It means OpGeneric Request....");
                }

                // Response to client.
                int stat, len;
                ipcp_stack_err_t err;
                const char *content = NULL;

                ASN1PEREncodeBuffer encodeBuffer(false);
                ASN1T_OpWLANModeReq_Response dataResponse;

                dataResponse.functionalControl = 0x03;
                ASN1C_OpWLANModeReq_Response OpWLANModeReq_Response(encodeBuffer, dataResponse);

                stat = OpWLANModeReq_Response.Encode();

                if (stat == 0)
                {
                    len = encodeBuffer.getMsgLen();
                    content = (char *)encodeBuffer.getMsgPtr();

                    ipcp_packet_t *result_packet = build_ipcp_response(SERVICE_ID_CONNECTIVITY, OP_ID_OpWLANModeReq,
                                                                        header->senderHandleId, IPCP_DATA_ENCODED,
                                                                        (void *)content, len);
                    if ((err = ipcp_send(peer_server, result_packet)) != STACK_SEND_MSG_SUCCESS)
                    {
                        ALOGE("%s: ipcp_send err : %d", __FUNCTION__, err);
                        break;
                    }
                } else {
                    encodeBuffer.printErrorInfo();
                    break;
                }
            }
            break;
        }
        case OP_ID_OpWLANLogSettingReq:
        {
            break;
        }
        case OP_ID_OpWLANSTADisCon:
        {
            break;
        }
        default:
        {
            break;
        }
    }

}


void error_cb(ipcp_packet_t *ipcp_packet, ipcp_stack_err_t error) {
    ALOGI("server recv... error_cb %d", error);
}

void init_server_config(ipcp_transport_conf_t *config) {
    config->service_id = SERVICE_ID_CONNECTIVITY;
    strcpy(config->host_ip, LOCAL_IP);
    config->host_port = LOCAL_PORT;
    config->sk_type = IPCP_TRANSPORT_UDP;
    config->vlan = SERVICE_VLAN_ID;
    config->priority = SERVICE_VLAN_PCP;
}

int main(int argc, char **argv) {

    ALOGI("start service");
    ipcp_transport_conf_t config;
    ipcp_stack_err_t error_code;

    init_server_config(&config);

    error_code = ipcp_setup(config, &callbacks);
    if (error_code != 0) {
        ALOGE("ipcp server side setup error: %d", error_code);
    } else {
        ALOGI("ipcpp server side setup success");
    }

    while (1) {
        sleep(1);
    }
    return 0;
}
